﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

#endregion

#region COPYRIGHT

/*
    Copyright (c) 2008, 2009, 2010 
        Roland Rosenkranz (Glatzemann@email.de)
*/

#endregion

#region LICENSE

/*
    This file is part of starLiGHT.Collision.

    starLiGHT.Collision is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    starLiGHT.Collision is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with starLiGHT.Collision.  If not, see <http://www.gnu.org/licenses/>.

    
    ADDITIONAL (commercial) LICENSES for starLiGHT.Collision are available on request.
*/

#endregion

#region Box2D Copyright
/*
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

#region Version Stuff
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 1486                  $:  Revision of last commit *
// * $Author:: glatzemann         $:  Author of last commit   *
// * $Date:: 2010-10-08 15:55:47 #$:  Date of last commit     *
// ************************************************************

// **************[ Box2D-Repository Info ]*********************
// Header-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Collision/Shapes/CircleShape.h
// Revision   : r109
// Change-Date: 2010-05-31
//
// Source-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Collision/Shapes/CircleShape.cpp
// Revision   : r109
// Change-Date: 2010-05-31
//
// Status     : Finished!
#endregion

namespace starLiGHT.Physic.TwoD
{
    /// <summary>A circle shape.</summary>
    public class CircleShape : Shape
    {
	    public CircleShape() 
        { 
            m_type = ShapeType.CIRCLE;
            m_radius = 0.0f;
            m_p = Vector2.Zero;
        }

        public override Shape Clone()
        {
            CircleShape clone = new CircleShape();
            clone.m_p = m_p;
            clone.m_radius = m_radius;
            clone.m_type = m_type;
            return clone;
        }

        public override int GetChildCount()
        {
            return 1;
        }

        /// <summary>Test a point for containment in this shape. This only works for convex shapes.</summary>
        /// <param name="p">point in world coordinates</param>
        /// <param name="xf">the shape world transform</param>
        public override bool TestPoint(ref Transform transform, Vector2 p)
        {
            Vector2 center = transform.position + Math.Mul(transform.R, m_p);
            Vector2 d = p - center;
            return Vector2.Dot(d, d) <= m_radius * m_radius;
        }

        /// <summary>Cast a ray against this shape. </summary>
        /// <param name="output">the ray-cast results. </param> 
        /// <param name="input">the ray-cast input parameters. </param> 
        /// <param name="transform">the transform to be applied to the shape. </param> 
        public override bool RayCast(out RayCastOutput output, ref RayCastInput input, ref Transform transform, int childIndex)
        {
            output = new RayCastOutput();

            Vector2 position = transform.position + Math.Mul(transform.R, m_p);
            Vector2 s = input.p1 - position;
            float b = Vector2.Dot(s, s) - m_radius * m_radius;
            
            // Solve quadratic equation.
            Vector2 r = input.p2 - input.p1;
            float c =  Vector2.Dot(s, r);
            float rr = Vector2.Dot(r, r);
            float sigma = c * c - rr * b;
            
            // Check for negative discriminant and short segment.
            if (sigma < 0.0f || rr < PhysicSettings.FLT_EPSILON)
            {
                return false;
            }
            
            // Find the point of intersection of the line with the circle.
            float a = -(c + (float)System.Math.Sqrt(sigma));
            
            // Is the intersection point on the segment?
            if (0.0f <= a && a <= input.maxFraction * rr)
            {
                a /= rr;
                output.fraction = a;
                output.normal = s + a * r;
                output.normal.Normalize();
                return true;
            }
            
            return false; 
        }

        /// <summary>Given a transform, compute the associated axis aligned bounding box for this shape.</summary>
        /// <param name="aabb">returns the axis aligned box.</param> 
        /// <param name="xf">the world transform of the shape.</param> 
        public override void ComputeAABB(out AABB aabb, ref Transform transform, int childIndex)
        {
#if !OPTIMIZE
            Vector2 p = transform.position + Math.Mul(transform.R, m_p);
            aabb.lowerBound = new Vector2(p.X - m_radius, p.Y - m_radius);
            aabb.upperBound = new Vector2(p.X + m_radius, p.Y + m_radius);
#else
            Vector2 p = transform.position;
            p.X = p.X + (transform.R.col1.X * m_p.X + transform.R.col2.X * m_p.Y);
            p.Y = p.Y + (transform.R.col1.Y * m_p.X + transform.R.col2.Y * m_p.Y);

            aabb.lowerBound = Vector2.Zero;
            aabb.lowerBound.X = p.X - m_radius; aabb.lowerBound.Y = p.Y - m_radius;
            aabb.upperBound = Vector2.Zero;
            aabb.upperBound.X = p.X + m_radius; aabb.upperBound.Y = p.Y + m_radius;
#endif
        }

        /// <summary>Compute the mass properties of this shape using its dimensions and density.
        /// The inertia tensor is computed about the local origin, not the centroid.</summary>
        /// <param name="massData">returns the mass data for this shape.</param> 
        /// <param name="density">the density in kilograms per meter squared.</param> 
        public override void ComputeMass(out MassData massData, float density)
        {
            massData.mass = density * PhysicSettings.b2_pi * m_radius * m_radius;
            massData.center = m_p;

            // inertia about the local origin
            massData.I = massData.mass * (0.5f * m_radius * m_radius + Vector2.Dot(m_p, m_p));
        }

        /// <summary>Get the supporting vertex index in the given direction.</summary>
        public Int32 GetSupport(Vector2 d)
        {
            //B2_NOT_USED(d);
            return 0;
        }

        /// <summary>Get the supporting vertex in the given direction.</summary>
        public Vector2 GetSupportVertex(Vector2 d)
        {
            //B2_NOT_USED(d);
            return m_p;
        }

        /// <summary>
        /// Get the vertex count.
        /// </summary>
        /// <returns></returns>
        public Int32 GetVertexCount()
        {
            return 1;
        }

        /// <summary>Get a vertex by index. Used by Distance.</summary>
        public Vector2 GetVertex(Int32 index)
        {
            //B2_NOT_USED(index);
            System.Diagnostics.Debug.Assert(index == 0);
            return m_p;
        }

        public Vector2 m_p;
    };
}
